package com.lry.pda.bottom.operation;

import com.lry.pda.topdown.SymbolDefine;

import java.util.*;

/**
 *  0.s -> e
 *  1.e -> e + t
 *  2.e -> t
 *  3.t -> t * f
 *  4.t -> f
 *  5.f -> ( e )
 *  6.f -> NUM
 */
public class Grammar {

    private Map<Integer, Symbols> symbolMap = new HashMap<>();
    private Map<Integer, List<Production>> productionMap = new HashMap<>();

    private static Grammar self;

    private Grammar(){
        initGrammar();
    }

    private void initGrammar() {
        int number = 0;
        //stat -> expr
        addProduction(false,new Production(0, SymbolDefine.STMT, Arrays.asList(SymbolDefine.EXPR),number++));

        //expr -> expr + term
        addProduction(false,new Production(0, SymbolDefine.EXPR, Arrays.asList(SymbolDefine.EXPR,SymbolDefine.PLUS,SymbolDefine.TERM),number++));

        //expr -> term
        addProduction(false,new Production(0, SymbolDefine.EXPR, Arrays.asList(SymbolDefine.TERM),number++));

        //term -> term * factor
        addProduction(false,new Production(0, SymbolDefine.TERM, Arrays.asList(SymbolDefine.TERM,SymbolDefine.TIMES,SymbolDefine.FACTOR),number++));

        //term -> factor
        addProduction(false,new Production(0, SymbolDefine.TERM, Arrays.asList(SymbolDefine.FACTOR),number++));

        //factor -> ( expr )
        addProduction(false,new Production(0, SymbolDefine.FACTOR, Arrays.asList(SymbolDefine.LP,SymbolDefine.EXPR,SymbolDefine.RP),number++));

        //factor -> NUM
        addProduction(false,new Production(0, SymbolDefine.FACTOR, Arrays.asList(SymbolDefine.NUM_OR_ID),number++));
    }

    private void addProduction(boolean nullable, Production production){

        productionMap.compute(production.getLeft(),(k,v)->{
            if(null==v){
                v = new ArrayList<>();
            }
            if(!v.contains(production)){
                v.add(production);
            }
            return v;
        });

        addSymbolMap(nullable,production);
    }

    private void addSymbolMap(boolean nullable, Production production){
        symbolMap.compute(production.getLeft(),(k,v)->{
            if(v==null){
                v = new Symbols(k,nullable);
            }
            v.addProduction(production.getRights());
            return v;
        });

        symbolMap.put(SymbolDefine.EOI,new Symbols(SymbolDefine.EOI,false));
        symbolMap.put(SymbolDefine.PLUS,new Symbols(SymbolDefine.PLUS,false));
        symbolMap.put(SymbolDefine.TIMES,new Symbols(SymbolDefine.TIMES,false));
        symbolMap.put(SymbolDefine.LP,new Symbols(SymbolDefine.LP,false));
        symbolMap.put(SymbolDefine.RP,new Symbols(SymbolDefine.RP,false));
        symbolMap.put(SymbolDefine.NUM_OR_ID,new Symbols(SymbolDefine.NUM_OR_ID,false));
    }

    public Map<Integer, List<Production>> getProductionMap() {
        return productionMap;
    }


    public Map<Integer, Symbols> getSymbolMap() {
        return symbolMap;
    }

    public static Grammar getSelf(){
        if(null==self){
            self = new Grammar();
        }
        return self;
    }


}
